package cn.edu.hnu.gpg.service;

import cn.edu.hnu.gpg.dao.AdministratorMapper;
import cn.edu.hnu.gpg.dao.StudentMapper;
import cn.edu.hnu.gpg.dao.TeacherMapper;
import cn.edu.hnu.gpg.dao.UserMapper;
import cn.edu.hnu.gpg.dto.UniversalUser;
import cn.edu.hnu.gpg.entity.*;
import cn.edu.hnu.gpg.entity.enums.UserType;
import cn.edu.hnu.gpg.util.PasswordUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.lang.NonNull;
import org.springframework.lang.Nullable;
import org.springframework.stereotype.Service;

import java.util.Base64;
import java.util.List;
import java.util.Random;

@Service
public class UserServiceImpl implements UserService {

    private static final int SALT_LENGTH = 5;

    private Random secureRandom = null;
    @Autowired
    public void setSecureRandom(Random secureRandom) {
        this.secureRandom = secureRandom;
    }

    private final UserMapper userMapper;
    private final StudentMapper studentMapper;
    private final TeacherMapper teacherMapper;
    private final AdministratorMapper administratorMapper;

    @Autowired
    public UserServiceImpl(UserMapper userMapper, StudentMapper studentMapper, TeacherMapper teacherMapper, AdministratorMapper administratorMapper) {
        this.userMapper = userMapper;
        this.studentMapper = studentMapper;
        this.teacherMapper = teacherMapper;
        this.administratorMapper = administratorMapper;
    }

    @Override
    public User findUserByUsername(String username) {
        if (username == null)
            return null;

        UserExample userExample = new UserExample();
        userExample.or().andUsernameEqualTo(username);
        List<User> users = userMapper.selectByExample(userExample);
        return users.size() > 0 ? users.get(0) : null;
    }

    @Override
    public UniversalUser getUniversalUserByUserId(Integer userId) {
        User user = userMapper.selectByPrimaryKey(userId);
        if (user == null)
            return null;

        switch (user.getUserType()) {
            case A:
                AdministratorExample administratorExample = new AdministratorExample();
                administratorExample.or().andUserIdEqualTo(userId);
                List<Administrator> administrators = administratorMapper.selectByExample(administratorExample);
                return administrators == null || administrators.isEmpty() ? null : new UniversalUser(administrators.get(0));

            case S:
                StudentExample studentExample = new StudentExample();
                studentExample.or().andUserIdEqualTo(userId);
                List<Student> students = studentMapper.selectByExample(studentExample);
                return students == null || students.isEmpty() ? null : new UniversalUser(students.get(0));

            case T:
                TeacherExample teacherExample = new TeacherExample();
                teacherExample.or().andUserIdEqualTo(userId);
                List<Teacher> teachers = teacherMapper.selectByExample(teacherExample);
                return teachers == null || teachers.isEmpty() ? null : new UniversalUser(teachers.get(0));
        }

        return null;
    }

    @Override
    public int createUser(@NonNull String username, @NonNull String rawPassword, @Nullable UserType type) {

        if (rawPassword == null)
            throw new IllegalArgumentException("rawPassword is null");

        if (rawPassword.isEmpty())
            throw new IllegalArgumentException("rawPassword is empty");

        User user = new User();
        user.setUsername(username);

        byte[] passwordSaltBytes = PasswordUtils.generateSalt(secureRandom, SALT_LENGTH);
        String passwordSaltBase64 = Base64.getEncoder().encodeToString(passwordSaltBytes);
        user.setPasswordSalt(passwordSaltBase64);
        user.setPasswordHash(PasswordUtils.encryptPassword(rawPassword, passwordSaltBytes));

        if (type == null) {
            type = UserType.S;
        }
        user.setUserType(type);

        int id = userMapper.insertSelective(user);
        if (id == 1)
            return user.getUserId();
        else return id;
    }

    @Override
    public boolean validatePassword(String username, String rawPassword) {
        User user = findUserByUsername(username);
        if (user == null)
            return false;

        String passwordHash = PasswordUtils.encryptPassword(rawPassword, user.getPasswordSalt());
        return user.getPasswordHash().equals(passwordHash);
    }

    @Override
    public boolean modifyPassword(String username, String oldPassword, String newPassword) {
        if (!validatePassword(username, oldPassword))
            return false;

        User user = findUserByUsername(username);
        user.setPasswordHash(PasswordUtils.encryptPassword(newPassword, user.getPasswordSalt()));

        return userMapper.updateByPrimaryKey(user) >= 1;
    }

    @Override
    public boolean modifyPassword(String username) {
        User user = findUserByUsername(username);
        user.setPasswordHash(PasswordUtils.encryptPassword("88888888", user.getPasswordSalt()));
        return userMapper.updateByPrimaryKey(user) >= 1;
    }

    @Override
    public boolean inactivateUser(String username) {

        if (username == null || username.isEmpty())
            return false;

        User user = new User();
        user.setActive(false);
        UserExample example = new UserExample();
        example.or().andUsernameEqualTo(username);

        int affectedLineCount = userMapper.updateByExampleSelective(user, example);

        return affectedLineCount > 0;
    }
}
